/*
 * $Id: DomHelper.java,v 1.2 2009/07/10 18:29:42 navdeep_mahajan Exp $
 * 
 * Copyright (C) 2008 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.common.util.xml.dom;

import java.util.Vector;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.ge.healthcare.autosc.common.exceptions.XmlParserException;

public class DomHelper {
	/**
	 * Get the node representing a section in the DOM. All sections are assumed
	 * to have unique labels. The returned node typically defines section of the
	 * document which is to be encoded in a configuration hash table.
	 * 
	 * @param sectionLabel
	 *            XML label for the specified section of the document.
	 *            Typically, the section is to be encoded in a configuration
	 *            hash table. Only one element in the entire document can use
	 *            the section label.
	 * 
	 * @return The node representing the specified section in the DOM.
	 * 
	 * @exception AscServiceAbortException
	 *                If an error is detected.
	 */
	public static Node getSectionNode(Document doc, String sectionLabel)
			throws XmlParserException {
		Node sectionNode = null;
		if (doc != null) {
			NodeList sectionNodeList = doc.getElementsByTagName(sectionLabel);
			verifyOneNodeInList(sectionNodeList, sectionLabel, "document");
			sectionNode = sectionNodeList.item(0);
		}
		return sectionNode;
	}

	/**
	 * Get the single child element from a single node. This gets a list of all
	 * the child elements from a single node and then verifies that there is
	 * only one element on the list. The child elements are the immediate
	 * children of the node which are element nodes.
	 * 
	 * @param myNode
	 *            The node from which to get the child element.
	 * 
	 * @return The single element node which is the immediate children of the
	 *         specified node.
	 * 
	 * @exception Exception
	 *                If an error is detected.
	 */
	public static Element getSingleChildElementFor(Node myNode)
			throws XmlParserException {
		Element[] childList = getChildElementsFor(myNode);
		if (childList.length != 1) {
			String message = new StringBuffer().append(
					"Unexpected number of elements under ").append(
					myNode.getNodeName()).append(
					" node:  Expected number: 1  Actual number: ").append(
					childList.length).toString();
			throw new XmlParserException(message);
		}
		return childList[0];
	}

	/**
	 * Get a list of child elements from a single node. These are the immediate
	 * children of the node which are element nodes.
	 * 
	 * @param myNode
	 *            The node from which to get the list of child elements.
	 * 
	 * @return The element nodes which are the immediate children of the
	 *         specified node.
	 * 
	 * @exception Exception
	 *                If an error is detected.
	 */
	public static Element[] getChildElementsFor(Node myNode) {
		Vector<Node> myList = new Vector<Node>();
		NodeList children = myNode.getChildNodes();
		for (int idx = 0; idx < children.getLength(); idx += 1) {
			Node currentNode = children.item(idx);
			if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
				myList.add(currentNode);
			}
		}
		Element[] resultList = new Element[myList.size()];
		myList.copyInto(resultList);
		return resultList;
	}

	/**
	 * Get a list of text items from a single node.
	 * 
	 * @param myNode
	 *            The node from which to get the text.
	 * 
	 * @return The list of text items from the specified node.
	 * 
	 * @exception Exception
	 *                If an error is detected.
	 */
	public static String[] getTextItemsFor(Node myNode) {
		Vector<String> myList = new Vector<String>();
		appendTextItemsFor(myNode, myList);
		String[] resultList = new String[myList.size()];
		myList.copyInto(resultList);
		return resultList;
	}

	/**
	 * Append the text items from a single node to a list of text items.
	 * Everything but element nodes and text nodes are ignored.
	 * 
	 * @param myNode
	 *            The node from which to get the text.
	 * 
	 * @param myList
	 *            The list to which the text items are to be appended.
	 * 
	 * @exception Exception
	 *                If an error is detected.
	 */
	private static void appendTextItemsFor(Node myNode, Vector<String> myList) {
		switch (myNode.getNodeType()) {
		case Node.ELEMENT_NODE:
			NodeList children = myNode.getChildNodes();
			for (int idx = 0; idx < children.getLength(); idx += 1) {
				appendTextItemsFor(children.item(idx), myList);
			}
			break;
		case Node.TEXT_NODE:
			String myText = myNode.getNodeValue();
			myText = myText.trim();
			if (myText.length() > 0) {
				myList.add(myText);
			}
			break;
		default:
			break;
		}
	}

	/**
	 * Verify that the specified node list has exactly one node.
	 * 
	 * @param listToCheck
	 *            Node list to check.
	 * 
	 * @param countType
	 *            Type of nodes being counted.
	 * 
	 * @param inType
	 *            Type of element whose nodes are being counted.
	 * 
	 * @exception Exception
	 *                If the expected size does not agree with the actual size
	 *                of the node list.
	 */
	public static void verifyOneNodeInList(NodeList listToCheck,
			String countType, String inType) throws XmlParserException {
		if (listToCheck.getLength() != 1) {
			String message = new StringBuffer().append("Unexpected number of ")
					.append(countType).append(" nodes in ").append(inType)
					.append(":  Expected number: 1  Actual number: ").append(
							listToCheck.getLength()).toString();
			throw new XmlParserException(message);
		}
	}

	/**
	 * Get a single named child element.
	 * 
	 * @param baseElement
	 *            Element expected to contain the specified child.
	 * 
	 * @param childName
	 *            Tag name of the child to retrieve.
	 * 
	 * @return The specified child element.
	 * 
	 * @exception Exception
	 *                If none, or multiple, child elements are found with the
	 *                specified name.
	 */
	public static Element getSingleChildElement(Element baseElement,
			String childName) throws XmlParserException {
		NodeList myList = baseElement.getElementsByTagName(childName);
		verifyOneNodeInList(myList, childName, baseElement.getTagName());
		return (Element) myList.item(0);
	}
}
